/*
PearReview - The peer review assistant.
Copyright (C) 2009  Dimitrij Pankratz, Anton Musichin
http://www.pearreview.com, info@pearreview.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.pearreview.application;

import java.io.Serializable;

public class Version implements Serializable, Comparable<Version> {
	private static final long serialVersionUID = 8861048780889983640L;
	
	private Integer major;
	private Integer minor;
	private Integer maintenance;
	private Integer build;
	
	public static final int MAJOR = 1;
	public static final int MINOR = 2;
	public static final int MAINTENANCE = 4;
	public static final int BUILD = 8;
	
	/**
	 * The number of distinct fields recognized by get and set.
	 * The fields are <code>1, 2, 4, ... 2<sup>FIELD_COUNT-1</cup></code>
	 */
	public static final int FIELD_COUNT = 4;
	
	public Version() {
		super();
	}	
	public Version(int major) {
		this();
		this.setMajor(major);
	}
	public Version(int major, int minor) {
		this(major);
		this.setMinor(minor);
	}
	public Version(int major, int minor, 
			int maintenance) {
		this(major, minor);
		this.setMaintenance(maintenance);
	}
	public Version(int major, int minor, 
			int maintenance, int build) {
		this(major, minor, maintenance);
		this.setBuild(build);
	}
	public Version(String version) {
		this();
		
		String[] x = version.split("\\.");
		
		for(int i = 0 ; i < Math.min(x.length, FIELD_COUNT) ; i++) {
			set(1<<i, new Integer(x[i]));
		}
	}
	
	protected void setMajor(Integer major) {
		this.major = major;
	}
	public Integer getMajor() {
		return major;
	}
	protected void setMinor(Integer minor) {
		this.minor = minor;
	}
	public Integer getMinor() {
		return minor;
	}
	protected void setMaintenance(Integer maintenance) {
		this.maintenance = maintenance;
	}
	public Integer getMaintenance() {
		return maintenance;
	}
	protected void setBuild(Integer build) {
		this.build = build;
	}
	public Integer getBuild() {
		return build;
	}
	
	protected void set(int field, Integer value) throws 
			IllegalArgumentException {
		switch(field) {
		case MAJOR: setMajor(value); break;
		case MINOR: setMinor(value); break;
		case MAINTENANCE: setMaintenance(value); break;
		case BUILD: setBuild(value); break;
		default: throw new IllegalArgumentException("Illegal field " + field);
		}
	}
	public Integer get(int field) throws IllegalArgumentException {
		switch(field) {
		case MAJOR: return getMajor();
		case MINOR: return getMinor();
		case MAINTENANCE: return getMaintenance();
		case BUILD: return getBuild();
		default: throw new IllegalArgumentException("Illegal field " + field);
		}
	}
	
	@Override
	public String toString() {
		return getVersion();
	}
	
	public String getVersion() {
		String versionString = new String();
		if(getMajor() != null) {
			versionString = getMajor().toString();
			if(getMinor() != null) {
				versionString += "." + getMinor().toString();
				if(getMaintenance() != null) {
					versionString += "." + getMaintenance().toString();
					if(getBuild() != null) {
						versionString += "." + getBuild().toString();
					}
				}
			}
		}
		return versionString;
	}
	
	public String getProductVersion() {
		String versionString = new String();
		if(getMajor() != null) {
			versionString = getMajor().toString();
			if(getMinor() != null) {
				versionString += "." + getMinor().toString();
			}
		}
		return versionString;
	}
	
	/**
	 * Checks if fields of version equals
	 * @param v version to check
	 * @param fields fields to check
	 * @return <code>true</code> if fields are the same 
	 * or <code>fields</code> is not set; <code>false</code> otherwise
	 */
	public boolean equals(Version v, int fields) {
		return compareTo(v, fields) == 0;
	}
	public boolean equals(Version v) {
		return equals(v, MAJOR | MINOR | MAINTENANCE | BUILD);
	}
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Version) {
			return equals((Version) obj);
		}
		return super.equals(obj);
	}
	
	public static Version parseVersion(String version) {
		return new Version(version);
	}
	
	/**
	 * Compares fields this version with specified version.
	 * @param v compares with this version
	 * @param fields fields to compare
	 * @return a negative integer, zero, or a positive integer as 
	 * this version is less than, equal to, or greater than the
	 * specified version.
	 */
	public int compareTo(Version v, int fields) {
		for(int bit = 0 ; bit < FIELD_COUNT ; bit++) {
			int field = 1 << bit;
			
			if(fields == 0) {
				break;
			} else {
				fields -= field;
			}
			
			Integer v1 = get(field);
			Integer v2 = v.get(field);
			
			if(v1 == null && v2 == null) {
				continue;
			}
			
			if(v1 == null) {
				return -1;
			}
			
			if(v2 == null) {
				return 1;
			}
			
			if(v1 - v2 == 0) {
				continue;
			}
			
			return v1 - v2;
		}
		
		return 0;
	}
	
	@Override
	public int compareTo(Version o) {
		return compareTo(o, MAJOR | MINOR | MAINTENANCE | BUILD);
	}
}
